/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

import * as fse from 'fs-extra';
import path from 'path';

import { CommonConst, ValidateRegExp } from '../../const/common-const.js';
import { TaskNames } from '../common/task-names.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { OhPackageJsonOpt, PackageJsonOpt } from '../task/ohos-har-task.js';

import { CoreProcessPackageJson } from './core-process-package-json.js';
import { ArtifactType } from './har-extend-info.js';
import Task = TaskNames.Task;
import { ModuleBuildProfile } from '../../options/build/module-build-profile.js';
import { FileUtil } from '../../utils/file-util.js';
import { ohPackageLoader } from '../../utils/loader/file/oh-package-loader.js';
import { OhosLogger }  from '../../utils/log/ohos-logger.js';
import { resolveJsonObjWithoutAtModule } from "../../validate/dependency-module-version-validate";

/**
 * 闭源Har打包发布前需要对package.json文件进行处理和增加一些信息
 * 当前该任务执行处理很快,先不需要做增量
 */
export class ProcessOhPackageJson extends CoreProcessPackageJson {
  private readonly generateOhPackageJsonFilePath: string;
  private readonly workerConfig: string[] | undefined;
  protected logger = OhosLogger.getLogger(ProcessOhPackageJson.name);

  constructor(taskService: TargetTaskService) {
    super(taskService, Task.PROCESS_OH_PACKAGE_JSON);
    this.generateOhPackageJsonFilePath = ohPackageLoader.getNodeOhPackagePath(path.resolve(this.generatePmDir, CommonConst.OH_PACKAGE_JSON5));
    this.workerConfig = this.processWorkerConfig();
  }

  protected doTaskAction(): void {
    super.doTaskAction();
    this.addWorkerData();
    this.addDynamicImport();
    this.processByteCodeHar();
    processResourceConfig(this.targetService, this.packageJsonObj);
    this.processInterfaceHar();
    resolveJsonObjWithoutAtModule(this.packageJsonObj);
    fse.outputJSONSync(this.generateOhPackageJsonFilePath, this.packageJsonObj);
  }

  protected addArtifactType(packageJsonObj: OhPackageJsonOpt) {
    packageJsonObj.artifactType = ArtifactType.OBFUSCATION;
  }

  private processByteCodeHar() {
    if (!this.targetService.isByteCodeHar()) {
      return;
    }
    const pkgJson = this.packageJsonObj as any;
    if (!pkgJson.metadata) {
      pkgJson.metadata = {};
    }
    pkgJson.metadata.byteCodeHar = true;
    delete pkgJson.main;
  }

  /**
   * 在打包har时，处理InterfaceHar字段
   * InterfaceHar字段对于har来说异常字段，会导致运行时crash，需要告警提示
   * @private
   */
  private processInterfaceHar() {
    const pkgJson = this.packageJsonObj as any;
    if (pkgJson?.packageType === 'InterfaceHar') {
      this.logger.warn(`"packageType": "InterfaceHar" in oh-package.json5 of har module '${this.moduleModel.getModule().getName()}' is not legal.`);
    }
  }

  /**
   * 向oh-package.json5里添加worker信息
   *
   * @protected
   */
  protected addWorkerData() {
    if (this.workerConfig) {
      (this.packageJsonObj as OhPackageJsonOpt).metadata = {
        ...((this.packageJsonObj as OhPackageJsonOpt).metadata ?? {}),
        workers: this.workerConfig,
      };
    }
  }

  /**
   * 处理worker信息
   *
   * @returns {string[] | undefined}
   * @private
   */
  private processWorkerConfig() {
    const workerConfig = this.targetService.getBuildOption().sourceOption?.workers;

    // 闭源HAR和字节码HAR都需要改后缀，其中字节码HAR为ts，闭源HAR为js
    if ((this.targetService.isByteCodeHar() || this._harExtendInfo.isObfuscatedHar()) && workerConfig) {
      const fileSuffix = this.hasUseTsHarField() ? '.ts' : '.js';
      return workerConfig.map((workerFile) => {
        const workerFileExt = path.extname(workerFile);
        return `${workerFile.substring(0, workerFile.lastIndexOf(workerFileExt))}${fileSuffix}`;
      });
    } else {
      return workerConfig;
    }
  }

  /**
   * 构建har包时需要将dynamicImport写入har产物oh-package.json5的metadata中
   * 闭源har模块需要保证以相对路径形式导入的文件后缀为js格式写入
   * 包名,文件名,文件夹相对路径保持不变写入
   * @protected
   */
  protected addDynamicImport() {
    const runtimeOnlyObj = this.targetService.getBuildOption()?.arkOptions?.runtimeOnly;
    if (!runtimeOnlyObj) {
      return;
    }
    const fileSuffix = this.hasUseTsHarField() ? '.ts' : '.js';
    const runtimeOnlySources: string[] = [];

    if (runtimeOnlyObj.sources) {
      // 相对路径形式导入处理
      runtimeOnlyObj.sources.forEach((eachSource) => {
        if (ValidateRegExp.RELATIVE_PATH_REG_EXP.test(eachSource)) {
          const absolutePath = path.resolve(this.moduleModel.getModule().getNodeDir(), eachSource);
          const fileStat = fse.statSync(absolutePath);
          if (fileStat.isDirectory()) {
            runtimeOnlySources.push(eachSource);
          }
          if (fileStat.isFile()) {
            if (!this.targetService.isOpenSource() && !ValidateRegExp.JS_FILE_SUFFIX_REG_EXP.test(FileUtil.getFileSuffix(eachSource))) {
              // 闭源har包构建时非js格式文件以相对路径导入需要改为js格式
              runtimeOnlySources.push(`${eachSource.substring(0, eachSource.lastIndexOf('.'))}${fileSuffix}`);
            } else {
              runtimeOnlySources.push(eachSource);
            }
          }
        }
      });
    }

    // 包名形式导入处理
    let runtimeOnlyPackages: string[] = [];
    if (runtimeOnlyObj.packages) {
      runtimeOnlyPackages = runtimeOnlyObj.packages;
    }

    (this.packageJsonObj as OhPackageJsonOpt).metadata = {
      ...((this.packageJsonObj as OhPackageJsonOpt).metadata ?? {}),
      runtimeOnly: {
        sources: runtimeOnlySources,
        packages: runtimeOnlyPackages,
      },
    };
  }
}

/**
 * 如果有定制resource，则需要resource写入到oh-package.json的metadata中resource
 * 不支持绝对路径
 *
 * @private
 */
export function processResourceConfig(targetService: TargetTaskService, packageJsonObj: PackageJsonOpt | OhPackageJsonOpt) {
  let directories = targetService.getTargetData().getTargetOpt().resource?.directories;
  if (directories && directories.length) {
    directories = directories.filter((dir) => !path.isAbsolute(dir));
    if (directories.length) {
      const resourceConfig: ModuleBuildProfile.ResourceBuildOpt = { directories: directories };
      (packageJsonObj as OhPackageJsonOpt).metadata = {
        ...((packageJsonObj as OhPackageJsonOpt).metadata ?? {}),
        resource: resourceConfig,
      };
    }
  }
}
